<?php

/**
 * api 工具
 * @package
 * @version 1.0:
 * @copyright 2003-2009 ShopEx
 * @author dreamdream
 * @license Commercial
 */
class api_utility
{

    var $host; //要坊问的 API host
    var $file; //要坊问的 API 路径
    var $port = 80; //端口
    var $tolken; //服务器的会话KEY
    var $error;//error
    var $error_no;//error
    var $debug;//debug
    var $gzip = false;
    var $send_type = 'json';
    var $application_error = null;
    var $call_func = null;
    var $call_params = array();

    function api_utility($host, $file, $port, $tolken)
    {
        if (!$this->system) {
            $this->system = &$GLOBALS['system'];
        }
        $host = strtolower($host);
        error_reporting(E_USER_ERROR | E_ERROR | E_USER_WARNING);
        $api_error = set_error_handler(array(&$this, "apiErrorHandle"));
        if (substr(0, 4) != 'http') {
            $host = 'http://' . $host;
        }
        $host_info = parse_url($host);
        if ($host_info['path'] == '/' || !$host_info['path']) {
            $this->host = $host_info['host'];
            $this->file = $file;
        } else {
            $this->host = $host_info['host'];
            if ($host_info['path']{(strlen($host_info['path']) - 1)} != '/') {
                $this->file = $host_info['path'] . $file;
            } else {
                $this->file = substr($host_info['path'], 0, -1) . $file;
            }
        }
        $this->tolken = $tolken;
        restore_error_handler();
    }

    /**
     * 数据加密
     * @param Array 被加密的源数据值
     * @return Array 数据加上加完密的值
     */
    function encode($vars)
    {
        if (constant('DEBUG_API')) {
            $vars['api_debug'] = 1;
        }
        ksort($vars);
        $verify = '';
        foreach ($vars as $key => $v) {
            $verify .= $v;
        }
        $vars['ac'] = Md5($verify . $this->tolken);
        return $vars;
    }


    function apiErrorHandle($errno, $errstr, $errfile, $errline)
    {


        switch ($errno) {
            case E_USER_ERROR:
                $this->error = 'user error:' . $errstr;
                //$this->error_handle('system error','user error:'.$errstr);
                break;
            case E_USER_WARNING:
                $this->error = 'user warning:' . $errstr;
                break;
            case E_ERROR:
                $this->error = 'error:' . $errstr;
                break;
            default:
                $this->error = 'system error' . $errstr;
                break;
        }
        return false;
        /*
        switch ($errno) {
            case E_USER_ERROR:
              $this->api_post_send('system error','user error:'.$errstr);
              break;
            case E_USER_WARNING:
              $this->api_post_send('system error','user warning:'.$errstr);
              break;
            case E_USER_NOTICE:
              $this->api_post_send('system error','user notice:'.$errstr);
              break;
            default:
              $this->api_post_send('system error',$errstr);
              break;
        }*/
    }

    /**
     * 发送接口
     * @param String 发送的数据源
     * @param String 发送方式get/post
     * @author DreamDream
     * @return Bool 成功/失败
     */
    function send($vars, $method = "post")
    {
        if ($vars['return_data']) {
            $this->send_type = $vars['return_data'];
        }
        $vars = $this->data_encode($vars);

        $vars = $this->encode($vars);
        $sender_vars = "";

        foreach ($vars as $key => $value) {
            $sender_vars .= $key . '=' . rawurlencode($value) . '&';
        }
        $sender_vars = substr($sender_vars, 0, -1);
        if ($method == 'post') {
            return $this->data_resume($this->api_post_send($sender_vars), $vars['return_data']);
        }
        if ($method == 'get') {
            return $this->data_resume($this->api_get_send($sender_vars), $vars['return_data']);
        }
    }

    /**
     * post 发送
     * @param String 发送的数据源
     * @return String 接收到的返回值
     */
    function api_post_send($vars)
    {
        $process = fsockopen($this->host, $this->port, $errno, $errstr, 10);
        if (!$process) {
            return false;
        }

        $post .= "POST " . $this->file . " HTTP/1.1\r\n";
        $post .= "Host: " . $this->host . ":" . $this->port . "\r\n";
        $post .= "Content-Type: application/x-www-form-urlencoded\r\n";
        if (function_exists('gzuncompress')) {
            $post .= "Content-Encoding: gzip \r\n";
        }
        $post .= "Content-Length: " . strlen($vars) . "\r\n";
        $post .= "Connection: Close\r\n\r\n";
        $post .= $vars;

        if (constant('DEBUG_API')) {
            error_log($vars . "\n", 3, HOME_DIR . "/logs/api.log");
        }

        fwrite($process, $post);
        while (!feof($process)) {
            $buf = trim(fgets($process, 1024));
            if (!$header_checked && !preg_match('/HTTP+[\s\S]+200/', $buf)) {
                $this->error = 'HTTP status is not 200;' . $buf;
                return false;
            } else {
                $header_checked = true;
            }
            if (!$this->gzip && preg_match('/gzip/', $buf)) {
                $this->gzip = true;
            }

            if ($buf == "") {
                break;
            }
            $tmp_buf_key_value = explode(": ", $buf);
            if ($tmp_buf_key_value[0] == "Content-Length") // ?
            {
                $clen = $tmp_buf_key_value[1];
            } else if ($tmp_buf_key_value[0] == "Transfer-Encoding" && $tmp_buf_key_value[1] == "chunked") {
                $clen = -1;
            } else if ($clen == 0) {
                $clen = "NO_SEARCH_LEN";
            }
        }

        $return_data = "";
        if ($clen == "NO_SEARCH_LEN") {
            while (!feof($process)) {
                $return_data .= trim(fgets($process, 1024));
            }
        } else if ($clen == 0) {
        } else if ($clen <= -1) {
            $loop_times1 = 0;
            while (1) {
                $clen = trim(fgets($process, 128));
                if (!$clen) {
                    $clen = trim(fgets($process, 128));
                    if (!$clen) {
                        break;
                    }
                }
                $clen = base_convert($clen, 16, 10);
                if ($clen > 0) {
                    $tmp_data = "";
                    $loop_times2 = 0;
                    while (1) {
                        $need_len = $clen - strlen($tmp_data);
                        if ($need_len <= 0) {
                            break;
                        }
                        $tmp_data .= @fread($process, $need_len);
                        if ($loop_times2++ >= 10000) {
                            break;
                        }
                    }
                    $return_data .= $tmp_data;
                }
                if ($loop_times1++ >= 1000) {
                    break;
                }
            }
        } else {
            $return_data = "";
            $loop_times2 = 0;
            while (1) {
                $need_len = $clen - strlen($return_data);
                if ($need_len <= 0) {
                    break;
                }
                $return_data .= @fread($process, $need_len);
                if ($loop_times2++ >= 10000) {
                    break;
                }
            }
        }
        fclose($process);
        if ($this->gzip && function_exists('gzuncompress')) {
            return @gzuncompress($return_data);
        } else {
            return $return_data;
        }
    }

    /**
     * get 发送
     * @param String 发送的数据源
     * @return String 接收到的返回值
     */
    function api_get_send($vars)
    {
        $process = fsockopen($host, 80, $errno, $errstr, 10);
        if (!$process) {
            return false;
        }
        $get = "GET " . $this->file . " HTTP/1.1\r\n";
        $get .= "Host: " . $this->host . ":" . $this->port . "\r\n";
        $get .= "Connection: Close\r\n\r\n";
        fwrite($process, $get);
        while (!feof($process)) {
            $result = fread($process, 1024);
        }
        fclose($process);
        return $result;
    }


    function data_resume($data, $code = 'json')
    {

        switch ($code) {
            case 'raw':
                return $data;
                break;
            case 'json':

                $result = json_decode($data, true);
                //$this->_empty_varfy($result);
                break;
            case 'soap':


                break;

            default:
                $xml = $this->system->loadModel('utility/xml');
                $result = $xml->xml2array($data, 'shopex');

        }
        $this->error_no = $result['msg'];

        if (constant('DEBUG_API')) {
            if ($result['request_info']) {
                $this->debug = $result['request_info'];
            }
            error_log(print_r($result, true) . "\n\n", 3, HOME_DIR . "/logs/api.log");
        }

        $this->application_error_handle($result);
        if ($result['result'] == 'success') {
            return $result['info'];
        } else if ($result['result'] == 'fail') {
            $this->error = $result['info'];
        } else if (!is_array($result)) {
            $this->error = 'data invalid';
            $this->error_no = '0x017';
        }
        // 统一触发平台api错误 wubin 2009-11-09 15:21
        $this->trigger_all_errors($this->call_func, $this->call_params);
        return false;
    }

    function _empty_varfy(&$data)
    {
        foreach (array($data) as $key => $value) {
            if (is_array($value)) {
                $this->_empty_varfy($value);
            } else if (is_null($data[$key])) {
                $data[$key] = "";
            }
        }
        return $data;
    }

    function application_error_handle($result)
    {
        if (is_array($result['application_error'])) {
            foreach ($result['application_error'] as $key => $value) {
                $this->application_error[] = $result['application_error'][$key];
                if ($result['application_error'][$key]['level'] == 'error') {
                    $error = true;
                }
            }
            if ($error) {
                $this->error = 'data error';
                return false;
            }
        }
        return true;
    }


    function data_encode($data)
    {
        $this->en_prase_data($data);
        $xml = $this->system->loadModel('utility/xml');
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                if ($this->send_type == 'json') {
                    $data[$key] = json_encode($value);
                }
                if ($this->send_type == 'xml') {
                    $result = $xml->array2xml($value, 'shopex');
                    $data[$key] = $result;
                }
            }
        }
        return $data;
    }

    function en_prase_data(&$data)
    {
        foreach ((array)$data as $key => $value) {
            if (preg_match("/^[\x7f-\xff]/", $key)) {
                $data['shop_prase_name'] = $key;
                $data['shop_prase_content'] = $value;
            }
            if (is_array($value)) {
                $this->en_prase_data($value);
            }
        }
        return $data;
    }

    function de_prase_data(&$data)
    {
        foreach ((array)$data as $key => $value) {
            if ($key == 'shop_prase_name') {
                $data[$data['shop_prase_name']] = $data[$data['shop_prase_content']];
            }
            if (is_array($value)) {
                $this->de_prase_data($value);
            }
        }
    }

    function data_decode(&$data)
    {
        foreach ($data as $key => $value) {
            if ($value{0} == '{') {
                $data[$key] = json_decode($value);
            }
        }
        $this->de_prase_data($data);
    }

    /**
     * 获取api数据
     *
     * @param string $api_name ，api接口名
     * @param string $api_version ，api的版本
     * @param array $send ，api请求的参数
     * @param string $return_data ，API的返回类型 xml/json
     * @param boolean $license ，是否要发送license，默认要发送
     * @param boolean $cache ，是否要缓存数据到本类对象中，默认不开
     *
     * @return mixed
     */
    function _getApiData($act, $api_version, $send = array(), $license = true, $return_data = 'json', $send_data = 'json')
    {
        if ($license) {
            $license = $this->system->getConf('certificate.id');
            if ($license) {
                $send = array_merge(array('certificate_id' => $license), (array)$send);
            }
        }
        $send = array_merge(array('act' => $act, 'api_version' => $api_version, 'send_data' => $send_data, 'return_data' => $return_data), (array)$send);
        return $this->send($send);
    }

    /**
     * 获取api数据
     *
     * @param string $api_name ，api接口名
     * @param string $api_version ，api的版本
     * @param array $params ，api请求的参数
     * @param string $return_data ，API的返回类型 xml/json
     * @param boolean $license ，是否要发送license，默认要发送
     * @param boolean $cache ，是否要缓存数据到本类对象中，默认不开
     *
     * @return mixed
     */
    function getApiData($api_name, $api_version, $params, $license = true, $cache = false, $return_data = 'json', $send_data = 'json')
    {
        if ($this->system->getConf('site.api.maintenance.is_maintenance') && floatval(API_VERSION) >= 2) {
            $this->error_no = '0x019';
            $this->error = $this->system->getConf('site.api.maintenance.notify_msg');
            trigger_error($this->error, E_USER_ERROR);
            return false;
        }
        $this->error_no = null;
        $this->error = null;
        if ($cache) {
            $key = md5($api_name . $api_version . json_encode($params));
            if (isset($this->api_data[$key])) {
                return $this->api_data[$key];
            } else {
                $this->api_data[$key] = $this->_getApiData($api_name, $api_version, $params, $license, $return_data, $send_data);
                return $this->api_data[$key];
            }
        } else {
            return $this->_getApiData($api_name, $api_version, $params, $license, $return_data);
        }
    }

    /**
     * trigger所有api 错误
     * 对于系统级错误和应用级错误error级别直接 trigger_error E_USER_ERROR
     *
     * @author bryant
     * @date 2009-05-31
     * @param int $call_func , 错误处理回调函数
     * @param int $call_params , api 错误处理回调函数参数
     */
    function trigger_all_errors($call_func = null, $call_params = array())
    {
        $errorStr = '';
        $callback = false;
        $error_type = E_USER_ERROR;
        if ($this->error_no == '0x003') {
            $errorStr = $this->error . '::';
            $callback = true;
        } else if ($this->error_no == '0x015') { // b2b端正在做还原备份 wubin 2009-11-09 15:35
            $errorStr = $this->error_no;
            $callback = true;
        } else if ($this->error_no == '0x016') { // 修改更新权限同步时的错误 yanglish
            $error_type = E_USER_WARNING;
        } else if ($this->error_no != null) {
            $errorStr = 'sys:' . $this->error;
            $callback = true;
        }

        //应用级错误没有编号,返回成功
        if ($this->application_error) {
            foreach ($this->application_error as $array_error) {
                if ($array_error['level'] == 'error') {
                    $errorStr = 'sys:' . $array_error['desc'];
                    $callback = true;
                    break;
                }
            }
        }

        if ($call_func && $errorStr && $callback === true) {
            @call_user_func_array($call_func, $call_params);
            $error_type = E_USER_WARNING;
        }
        if ($errorStr) {
            trigger_error($errorStr, $error_type);
        }
    }
}

?>